home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Snippets / ShowInitAndName / ShowInitAndName.c next >
C/C++ Source or Header  |  1996-01-12  |  58KB  |  1,296 lines

  1. #include <Gestalt.h> /* Previously named <GestaltEqu.h> */
  2. #include <Memory.h>
  3. #include <Quickdraw.h>
  4. #include <Resources.h>
  5. #include <Notification.h>
  6.  
  7. #include "ShowInitAndName.h"
  8.  
  9.  
  10. /* IMPORTANT: Read "ShowInitAndName.h" first!! */
  11. /*
  12.    As soon as possible after your init is called, be sure to show an icon.
  13.    This will signify to the user that your init is just starting to load.
  14.    
  15.    ShowInitAndName ( 128, "Loch Ness Extension", nil, noErr, false );
  16.   
  17.    As the very last thing your init does, show an icon again (this time indicating you’re allDone).
  18.    This will signify to the user that your init is finished loading.
  19.    
  20.    ShowInitAndName ( 128, "Loch Ness Extension", nil, noErr, true );
  21.    
  22.    If you follow these guidelines, the user can see exactly when your init starts,
  23.    and exactly when your init ends. That should visually absolve your init of pauses, freezes, or
  24.    startup crashes which are actually THE FAULT of inits before and after yours.
  25. */
  26.  
  27.  
  28.  
  29. OSErr ShowInitAndName ( short iconResourceID, char *extensionNameInCFormat, char *statusMessageInCFormat, OSErr errorEncounteredIfAny, Boolean allDone )
  30. {
  31.      /*
  32.       ShowInitAndName displays an icon and extension name at the screen
  33.       location appropriate for the current system extension (init).
  34.       
  35.       All parameters are sent in [--->]
  36.       
  37.       short           iconResourceID;  The resource manager id of the icon representing your system extension (init).
  38.                                        (You may pass kIconResourceIDNone for no icon.)
  39.                                        The icon ('icl8', 'icl4', 'cicn', or 'ICN#') will be drawn in color
  40.                                        if a color resource is available and the screen location is color.
  41.                                        The icon with iconResourceID will be drawn crossed out if
  42.                                        errorEncounteredIfAny ≠ noErr and allDone is true.
  43.                                        
  44.       char *  extensionNameInCFormat;  The name of your system extension (init).
  45.                                        (You may pass nil for no name.)                                 
  46.                                        This is in c-language string format (string ends with 0 byte).
  47.                                        The extension name will be displayed beneath the icon.
  48.  
  49.       char *  statusMessageInCFormat;  A status message like "Attempting to install driver", "Driver installed successfully", etc.
  50.                                        (You may pass nil for no message.)
  51.                                        This is in c-language string format (string ends with 0 byte).
  52.                                        You should assume these messages will be rapidly displayed to the
  53.                                        user in future systems. If errorEncounteredIfAny ≠ noErr,
  54.                                        statusMessageInCFormat ≠ nil, and allDone is true, a notification manager
  55.                                        routine will be called with the statusMessageInCFormat.
  56.                                        Put more simply, an alert will appear with your message if your final
  57.                                        call to ShowInitAndName includes an error code.
  58.                                        
  59.       OSErr    errorEncounteredIfAny;  The system error encountered (if any) as the extension is installing. 
  60.                                        (You may pass noErr for... uh... no error.)
  61.                                        
  62.       Boolean                allDone;  If not false, the screen position will be advanced to the
  63.                                        next appropriate location in preparation for the next init.
  64.                                        (You may pass false to keep in the same location.) 
  65.                                        Pass false to not advance the screen position (often used
  66.                                        to draw different icons at the same location for animation.)
  67.       
  68.       returns OSErr;
  69.                        noErr (0)  No error
  70.               resNotFound (-192)  iconResourceID not found ('icl8', 'icl4', 'cicn', or 'ICN#')
  71.               [This routine may also return the result of MemError() or ResError().]
  72.               
  73.               
  74.       *** This routine MOVES MEMORY ***
  75.       
  76.       See also: ShowInitAndPascalName for the exact same function with a Pascal string format filename.
  77.       
  78.       
  79.       96/01/11   v1.0.0   David Cook   First version  
  80.      */
  81.         
  82.    
  83.            
  84.      QDGlobalsPlusAStartForTheLocalA5 localQDGlobals; /* Our own personal QD globals. Needed for 68K code as well as InitGraf calls at 'INIT' time. */
  85.       
  86. #ifndef powerc
  87.      long originalA5Register; /* Original contents of register A5 when this routine was started. 68K code only. */
  88. #endif
  89.  
  90.      ShowInitOverrideUPP showInitOverride; /* Used to call the system based ShowInitAndName if available. */
  91.  
  92.      GrafPort localPort;   /* Port to draw the icon into if main screen is B&W */
  93.      CGrafPort localCPort; /* Port to draw the icon into if main screen is color */
  94.         
  95.      short pixelDepthOfMainScreen; /* 2^pixelDepth = number of colors (or 1 for b&w). */
  96.      
  97.      short horizontal; /* Left point for starting to draw text or icon */
  98.      short vertical;   /* Top point for starting to draw icon */
  99.      
  100.      short widthOfText;
  101.      short largerOfTextOrIconWidth;
  102.      
  103.      short iconCenteringValue;
  104.      
  105.      Rect iconDestinationRect;
  106.      Rect textCStringDestinationRect;
  107.                
  108.      OSErr result; /* The error result (or noErr) of this entire function */
  109.      
  110.      
  111.      
  112.      
  113. #ifndef powerc
  114.           /* 680x0-chip based Macs (68K) require some registers to be set in a certain way for globals and Quickdraw */
  115.      originalA5Register = SetA5 ( (long) &(localQDGlobals.setTheLocalA5ToHere) );
  116. #endif
  117.      
  118.      result = noErr;
  119.  
  120.  
  121.      /* This is very cool. This allows a system routine to show our init icon and name instead of us.
  122.         This might be very useful in future operating systems where the init is shown differently.    */
  123.         
  124.      if ( Gestalt ( gestaltShowInitAndName, (long *) &showInitOverride ) == noErr )
  125.      {
  126.           if ( showInitOverride != nil )
  127.           {
  128.                result = CallShowInitOverrideProc ( showInitOverride,iconResourceID, extensionNameInCFormat, statusMessageInCFormat, errorEncounteredIfAny, allDone, 0 );
  129.           }
  130.      }
  131.  
  132.  
  133.      if ( result == kShowInitHasOverridden_NoNeedForLocalCode )
  134.      {
  135.           result = noErr;
  136.      }
  137.      else if ( result == noErr )
  138.      {     
  139.           /* Initialize our localQDGlobals structure */
  140.           /* This prepares Quickdraw for all of our drawing calls */
  141.           InitGraf ( &localQDGlobals.thePort ); 
  142.           InitFonts ( );
  143.      
  144.      
  145.           pixelDepthOfMainScreen = GetMainScreenPixelDepth ( );
  146.           
  147.           if ( pixelDepthOfMainScreen > 1 )
  148.           {
  149.                OpenCPort ( &localCPort ); /* The main screen is color */
  150.                /* If we don’t open a color port, System 7’s PlotIconID will only draw B&W icons. */
  151.           }
  152.           else
  153.           {
  154.                OpenPort ( &localPort ); /* The main screen (or Quickdraw) is B&W */
  155.           }
  156.           
  157.           
  158.           /* We start with the assumption that we are drawing just the width of an icon */
  159.           largerOfTextOrIconWidth = kWidthOfIcon;
  160.           
  161.           widthOfText = GetPixelWidthOfTextCString ( extensionNameInCFormat ) + kTextCStringHorizontalBorderWhiteSpace;     
  162.      
  163.           if ( widthOfText > kMaximumTotalPixelWidthOfAShowInit ) /* The absolute pixel width limit we’ll allow a filename to be */
  164.           {
  165.                widthOfText = kMaximumTotalPixelWidthOfAShowInit; 
  166.           }
  167.           
  168.           if ( widthOfText > largerOfTextOrIconWidth )
  169.           {
  170.                largerOfTextOrIconWidth = widthOfText;
  171.           }
  172.      
  173.      
  174.      
  175.           /* Get the current horizontal and vertical location for drawing this init */ 
  176.           LMGetShowInitHorizontalAndVertical ( &horizontal, &vertical );
  177.                     
  178.           /* Knowing how much horizontal and vertical space we need,      */
  179.           /* this makes sure the horizontal and vertical position won’t   */
  180.           /* cause us to draw off the edge of the main screen.            */
  181.           CorrectShowInitHorizontalAndVerticalIfNotOnMainScreen ( &horizontal, largerOfTextOrIconWidth,
  182.                                                                   &vertical,   kHeightOfIcon + kTextCStringHeight );
  183.                                                                   
  184.            
  185.           /* Where do we want to draw the icon? (assuming it has a regular width and height) */
  186.           iconDestinationRect.top = vertical;
  187.           iconDestinationRect.bottom = iconDestinationRect.top + kHeightOfIcon;
  188.           iconCenteringValue = ( ( largerOfTextOrIconWidth - kWidthOfIcon ) / 2 ); /* So we can base textCStringDestinationRect on a iconDestinationRect in case it is altered externally by force. */
  189.           iconDestinationRect.left = horizontal + iconCenteringValue; /* Center the icon horizontally */
  190.           iconDestinationRect.right = iconDestinationRect.left + kWidthOfIcon;                                                       
  191.           
  192.           if ( iconResourceID != kIconResourceIDNone ) /* Does the caller really want us to draw an icon? */
  193.           {
  194.                DrawIcon ( iconResourceID, pixelDepthOfMainScreen, &iconDestinationRect );
  195.           }
  196.  
  197.           
  198.           if ( extensionNameInCFormat != nil ) /* Is there a filename or message to display? */
  199.           {
  200.                textCStringDestinationRect.top = iconDestinationRect.top + kHeightOfIcon;
  201.                textCStringDestinationRect.bottom = textCStringDestinationRect.top + kTextCStringHeight;
  202.                textCStringDestinationRect.left = iconDestinationRect.left - iconCenteringValue + ( ( largerOfTextOrIconWidth - widthOfText ) / 2 );
  203.                textCStringDestinationRect.right = textCStringDestinationRect.left + widthOfText - 1;
  204.           
  205.                /* Draw the filename or text message */
  206.                DrawTextCString ( extensionNameInCFormat, &textCStringDestinationRect );
  207.                
  208.                if ( allDone == false )
  209.                {
  210.                     InvertRect ( &textCStringDestinationRect );
  211.                     /* Hilite the text using invert.
  212.                        Note: Don’t use the hilite color, because text hilited
  213.                        with the hilite color appears to the user to be editable. */
  214.                }
  215.           }
  216.      
  217.      
  218.           if ( allDone != false && errorEncounteredIfAny != noErr )
  219.           {
  220.                DrawCrossOut ( &iconDestinationRect, pixelDepthOfMainScreen );
  221.                /* We cross out the icon after the filename (or text message) has been drawn, */
  222.                /* because having the filename overwrite the cross out would look strange. */
  223.                
  224.                if ( statusMessageInCFormat != nil )
  225.                {
  226.                     NotificationManagerMessage ( statusMessageInCFormat );
  227.                }
  228.           }
  229.      
  230.                
  231.           if ( pixelDepthOfMainScreen > 1 )
  232.           {
  233.                CloseCPort ( &localCPort );
  234.                /* Make sure you close a color port using CloseCPort, just to be safe! */
  235.           }
  236.           else
  237.           {
  238.                ClosePort ( &localPort );
  239.           }
  240.      
  241.      
  242.           if ( allDone != false )
  243.           {
  244.                /* Because of columns or not having enough room at the end of a row, we may have
  245.                  drawn in a different horizontal or vertical position than was actually stored,
  246.                  so update the storage to reflect our current drawing location.                 */
  247.                LMSetShowInitHorizontalAndVertical ( horizontal, vertical );
  248.           
  249.                AdvanceShowInit ( largerOfTextOrIconWidth );
  250.           }
  251.      }
  252.      
  253.      
  254.      
  255. #ifndef powerc
  256.       /* On 68K Macs, we started the routine by modifying a register. Let’s restore it. */
  257.      SetA5 ( originalA5Register );
  258. #endif
  259.      
  260.      return ( result );
  261. }
  262.  
  263.  
  264. OSErr ShowInitAndPascalName ( short iconResourceID, StringPtr extensionNameInPascalFormat, StringPtr statusMessageInPascalFormat, OSErr errorEncounteredIfAny, Boolean allDone )
  265. {
  266.      /* Accepts strings in Pascal format. Otherwise identical to ShowInitAndName.
  267.         See ShowInitAndName for details */
  268.      
  269.      char extensionNameInCFormat [ 255 + 1 ];
  270.      char statusMessageInCFormat [ 255 + 1 ];
  271.      
  272.  
  273.      /* Copy the strings to c-language format */
  274.      CopyCStringFromPascalStringWithLimit ( extensionNameInCFormat, extensionNameInPascalFormat, 255 );
  275.      CopyCStringFromPascalStringWithLimit ( statusMessageInCFormat, statusMessageInPascalFormat, 255 );
  276.      
  277.      /* Pass into ShowInitAndName */
  278.      return ( ShowInitAndName ( iconResourceID, extensionNameInCFormat, statusMessageInCFormat, errorEncounteredIfAny, allDone ) );
  279. }
  280.  
  281.  
  282.  
  283. /******************************** STRING ROUTINES ***************************************/
  284.  
  285. unsigned long GetLengthOfCStringWithLimit ( char *pointerToCString, unsigned long countNoMoreThanThisManyCharacters )
  286. {
  287.     /*
  288.       GetLengthOfCStringWithLimit can be used anywhere you need to get the length of a cstring.
  289.       This function has a parameter which limits how many characters will be counted.
  290.       The result is similar (but not identical) to:
  291.            unsigned long finalLength;
  292.            finalLength = strlen ( pointerToCString );
  293.            if ( finalLength > countNoMoreThanThisManyCharacters )
  294.            {
  295.                 finalLength = countNoMoreThanThisManyCharacters;
  296.            }
  297.       The difference is, if some idiot passes a Pascal string, or an unterminated c string,
  298.       strlen will continue searching for the end of the string until it is lucky enough to
  299.       run into a 0x00. This routine (which is slower), keeps track of the length, and stops
  300.       when it reaches countNoMoreThanThisManyCharacters. Also, you don’t need to include
  301.       any ANSI in your project to use this routine. Also, strlen ( nil ) bombs, but this doesn’t.
  302.       
  303.       char *             pointerToCString;  A c string which you want to know the length of.
  304.                                             This is in c-language string format (string ends with 0x00).
  305.                                             Pass nil to be returned a length of zero (no bombs!).
  306.          
  307.       unsigned long countNoMoreThanThisManyCharacters;  A maximum limit to the length you’ll allow
  308.                                             pointerToCString to be.
  309.       
  310.       returns an unsigned long, never greater than countNoMoreThanThisManyCharacters.
  311.               
  312.     
  313.       * This routine does NOT move memory *
  314.             
  315.       See also: strlen in any ANSI library.
  316.       
  317.             
  318.       
  319.       96/01/11   v1.0.0   David Cook   First version
  320.    */
  321.           
  322.      unsigned long numberOfCharactersInCString;
  323.      
  324.      
  325.      numberOfCharactersInCString = 0;
  326.      
  327.      if ( pointerToCString != nil )
  328.      {          
  329.           while ( numberOfCharactersInCString < countNoMoreThanThisManyCharacters && *pointerToCString != 0 )
  330.           {
  331.                pointerToCString++;
  332.                numberOfCharactersInCString++;
  333.           }
  334.      }
  335.      
  336.      return ( numberOfCharactersInCString );
  337. }
  338.  
  339.  
  340. void CopyCStringFromPascalStringWithLimit ( char *pointerToDestinationCString, StringPtr pointerToSourcePascalString, Byte copyNoMoreThanThisManyCharacters )
  341. {
  342.     /*
  343.       CopyCStringFromPascalStringWithLimit copies from a Pascal-language string format
  344.       (byte length followed by characters) to a c-language string format (string ends with 0x00),
  345.       converting as it copies. This has a parameter passed limit to the number of characters it is allowed
  346.       to copy, at which point it properly ends the c string.
  347.       
  348.       
  349.       char *    pointerToDestinationCString;   Space for a c string which you want copied from pointerToSourcePascalString.
  350.                                                This is in c-language string format (string ends with 0x00).
  351.                                                Make sure that the destination has enough space for 1 + copyNoMoreThanThisManyCharacters.
  352.                                                   char myBadCString [ 10 ];
  353.                                                   CopyCStringFromPascalStringWithLimit ( myBadCString, "\p123456789012345", 10 );
  354.                                                   NO! NO! NO!
  355.                                                   char myGoodCString [ 10 + 1 ];
  356.                                                   CopyCStringFromPascalStringWithLimit ( myGoodCString, "\p123456789012345", 10 );
  357.                                                   YES! YES! YES!
  358.                                                Pass nil to do nothing (but no bombs!).
  359.       
  360.       StringPtr pointerToSourcePascalString;   Original Pascal string which you want copied into pointerToDestinationCString.
  361.                                                This string is NOT altered in any way by this routine.
  362.                                                Pass nil to make an empty (0x00 terminated) pointerToDestinationCString. (no bombs!) 
  363.       
  364.       unsigned long copyNoMoreThanThisManyCharacters;  A maximum limit to the number of characters you’ll allow
  365.                                                pointerToDestinationCString to contain. Always 1 less than allocated
  366.                                                memory!
  367.                                                Pass 0 to make an empty (0x00 terminated) pointerToDestinationCString. 
  368.       
  369.       returns nothing.
  370.               
  371.     
  372.       * This routine does NOT move memory *
  373.       
  374.             
  375.       
  376.       96/01/11   v1.0.0   David Cook   First version
  377.    */
  378.      
  379.      Byte numberOfCharactersRemainingToBeCopied;
  380.      
  381.      if ( pointerToDestinationCString != nil ) /* Is there a destination string to copy to? */
  382.      {
  383.           if ( pointerToSourcePascalString != nil && copyNoMoreThanThisManyCharacters > 0 ) /* Is there a source string and do we want to copy any characters? */
  384.           {               
  385.                numberOfCharactersRemainingToBeCopied = (Byte) *pointerToSourcePascalString;
  386.                pointerToSourcePascalString++;
  387.                
  388.                if ( numberOfCharactersRemainingToBeCopied > copyNoMoreThanThisManyCharacters )
  389.                {
  390.                     numberOfCharactersRemainingToBeCopied = copyNoMoreThanThisManyCharacters;
  391.                }
  392.                                              
  393.                while ( *pointerToSourcePascalString != 0 && numberOfCharactersRemainingToBeCopied > 0 )
  394.                {
  395.                     *pointerToDestinationCString = *pointerToSourcePascalString;
  396.                     pointerToDestinationCString++;
  397.                     pointerToSourcePascalString++;
  398.                     numberOfCharactersRemainingToBeCopied--;
  399.                }
  400.           }
  401.           
  402.           *pointerToDestinationCString = 0;
  403.      }
  404. }
  405.  
  406.  
  407.  
  408. /******************************** NOTIFICATION MANAGER ***************************************/
  409.  
  410. OSErr NotificationManagerMessage ( char *messageInCFormat )
  411. {
  412.      /* A quick way to send a single c-format string message to the user.
  413.      
  414.         When you need to notify the user why the init failed to start up, don’t just show them
  415.         a crossed out icon, send them message by the Notification Manager.
  416.         
  417.         Oh, by the way, a copy of the string message is PERMANENTLY allocated into the system heap
  418.         so that the Notification Manager may deliver the message to the user after the Mac has
  419.         finished starting up. After calling this routine, you may safely close your
  420.         resource fork and exit your init without losing the message that has been sent by this
  421.         routine (because the message is PERMANENTLY allocated elsewhere in the system heap.).
  422.         
  423.         WARNING: The memory for the message is only freed after the user restarts again.
  424.         This should not pose a problem, because the message will be truncated to a maximum 255 bytes (+queueElement)
  425.         and this routine is usually called only after a bad startup.
  426.         
  427.         char *       messageInCFormat;   A c-format string (usually a complete error message including filename of sender).
  428.                                          This string is NOT altered in any way by this routine.
  429.                                          Pass nil or an empty string to not send a message (but no bombs!).
  430.         
  431.         returns OSErr;
  432.                        noErr (0)  No error
  433.                dsOldSystem (102)  Pre-System 6.0, so couldn’t call Notification manager
  434.                memFullErr (-108)  Couldn’t allocate memory for Notification manager call
  435.                  memPCErr (-114)  Was told noErr by MemError, but the allocated pointer was nil
  436.         
  437.         *** This routine MOVES MEMORY ***
  438.                   
  439.                   
  440.         96/01/11   v1.0.0   David Cook   First version
  441.    */
  442.    
  443.      unsigned long cStringSize;
  444.      
  445.      StringPtr pointerToDestinationPascalString;
  446.           
  447.           
  448.      /* I’ve created a combined structure so that no matter what 
  449.         structure alignment method you compile with (68K or PPC),
  450.         we know where the start of the string is. */
  451.      NMRecAndPascalStringCombinedRecord *notificationElementPtr;
  452.      
  453.      
  454.      OSErr result;
  455.      
  456.      
  457.  
  458.      if ( GetSystemVersion ( ) < kNotificationManagerMinimumSystemRequired )
  459.      {
  460.           result = dsOldSystem;
  461.      }
  462.      else if ( messageInCFormat == nil )
  463.      {
  464.           result = memPCErr;
  465.      }
  466.      else
  467.      {
  468.           result = noErr;
  469.      
  470.           cStringSize = GetLengthOfCStringWithLimit ( messageInCFormat, 255 );
  471.           
  472.           if ( cStringSize > 0 )
  473.           {
  474.                notificationElementPtr = ( NMRecAndPascalStringCombinedRecord * ) NewPtrSysClear ( sizeof ( NMRecAndPascalStringCombinedRecord ) + cStringSize + 1 );
  475.                /* We’ve allocated the notificationElement and Pascal string together,        */
  476.                /* because they are not going to be disposed of until the system is rebooted. */
  477.                /* It seems kinder to have a single Ptr hang around, than to have two.        */
  478.                
  479.                
  480.                result = MemError ( );
  481.                
  482.                if ( result == noErr )
  483.                {
  484.                     if ( notificationElementPtr == nil )
  485.                     {
  486.                          result = memPCErr; /* MemError lied to us */
  487.                     }
  488.                     else
  489.                     {
  490.                          notificationElementPtr->notificationRecord.qType = nmType;
  491.                          notificationElementPtr->notificationRecord.nmMark = 0;
  492.                          notificationElementPtr->notificationRecord.nmIcon = nil;
  493.                          notificationElementPtr->notificationRecord.nmSound = (Handle) -1;
  494.                          notificationElementPtr->notificationRecord.nmStr = (StringPtr) &(notificationElementPtr->notificationPascalString);
  495.                          notificationElementPtr->notificationRecord.nmResp = (NMUPP) -1;
  496.                          notificationElementPtr->notificationRecord.nmRefCon = kNotificationSentByShowInitAndName;                       
  497.                                                   
  498.                                                   
  499.                          pointerToDestinationPascalString = notificationElementPtr->notificationRecord.nmStr;
  500.                          
  501.                          *pointerToDestinationPascalString = cStringSize;
  502.                          pointerToDestinationPascalString++;
  503.                     
  504.                          while ( cStringSize > 0 )
  505.                          {
  506.                               *pointerToDestinationPascalString = *messageInCFormat;
  507.                               pointerToDestinationPascalString++;
  508.                               messageInCFormat++;
  509.                          
  510.                               cStringSize--;
  511.                          }
  512.                                              
  513.                          
  514.                          
  515.                          result = NMInstall ( &(notificationElementPtr->notificationRecord) );
  516.                          
  517.                          if ( result != noErr )
  518.                          {
  519.                               DisposePtr ( (Ptr) notificationElementPtr );
  520.                          }
  521.                     }
  522.                }
  523.           }
  524.      }
  525.      
  526.      return ( result );
  527. }
  528.  
  529.  
  530.  
  531.  
  532.  
  533. /******************************** SYSTEM CHECKS ***************************************/
  534.  
  535. Boolean IsColorQuickdrawAvailable ( void )
  536. {
  537.      /* IsColorQuickdrawAvailable returns true if Color Quickdraw exists on this Mac, false if it does not.   */
  538.      
  539.      /* 96/01/11   v1.0.0   David Cook   First version  */ 
  540.       
  541.      
  542.  
  543.      Boolean doesColorQuickdrawExist;
  544.           
  545.      long quickdrawVersionAnswer;
  546.      
  547.      doesColorQuickdrawExist = false;
  548.  
  549.      if ( Gestalt ( gestaltQuickdrawVersion, &quickdrawVersionAnswer ) == noErr )
  550.      {
  551.           if ( quickdrawVersionAnswer >= gestalt8BitQD )
  552.           {
  553.                doesColorQuickdrawExist = true;
  554.           }
  555.      }
  556.      
  557.      return ( doesColorQuickdrawExist );
  558. }
  559.  
  560.  
  561. long GetSystemVersion ( void )
  562. {
  563.      /* GetSystemVersion returns returns the System Software version present. i.e. 0x0700 for System 7,              */
  564.      /* 0x0605 for system 6.0.5. You’re not supposed to rely on system versions to determine features - yah yah yah  */
  565.      
  566.      /*  96/01/11   v1.0.0   David Cook   First version  */
  567.  
  568.  
  569.      long currentSystemVersion;
  570.  
  571.  
  572.      Gestalt ( gestaltSystemVersion, ¤tSystemVersion );
  573.      
  574.      return ( currentSystemVersion );
  575. }
  576.  
  577.  
  578. long GetMainScreenPixelDepth ( void )
  579. {
  580.      /* Don’t use this function for everyday application usage. This only tells the
  581.         depth of the MAIN screen, which is fine for inits, but not for REAL programs. */
  582.         
  583.      /* This returns the depth (i.e. 2 to the power of depth = number of colors) of the main screen. */
  584.      
  585.      /*  1 = Black and white     */
  586.      /*  2 = Four colors         */
  587.      /*  4 = Sixteen colors      */
  588.      /*  8 = 256 colors          */
  589.      /* 16 = Thousands of colors */
  590.      /* 32 = Millions of colors  */
  591.      
  592.      /* Note: The actual monitor itself could be gray scale, not color. This routine doesn’t check for that information - and shouldn’t. */
  593.      
  594.      /*  96/01/11   v1.0.0   David Cook   First version  */
  595.      
  596.      
  597.      long mainScreenPixelDepth;
  598.      
  599.      GDHandle mainDevicePtr;
  600.      
  601.      
  602.      mainScreenPixelDepth = 1; /* B&W */
  603.      
  604.      if ( IsColorQuickdrawAvailable ( ) != false )
  605.      {
  606.           mainDevicePtr = GetMainDevice ( );
  607.           
  608.           if ( mainDevicePtr != nil )
  609.           {
  610.                mainScreenPixelDepth = (*(*mainDevicePtr)->gdPMap)->pixelSize;
  611.           }
  612.      }
  613.           
  614.      return ( mainScreenPixelDepth );
  615. }
  616.  
  617.  
  618. void GetMainScreenRect ( Rect *mainScreenRectPtr )
  619. {
  620.      /* Don’t use this function for everyday application usage. This only tells the
  621.         size of the MAIN screen, which is fine for inits, but not for REAL programs. */
  622.         
  623.      /* This routine returns the main screen’s boundary rectangle.              */
  624.      /* You can pass a nil handle for mainScreenRectPtr and not cause a crash. */
  625.      
  626.      /*  96/01/11   v1.0.0   David Cook   First version  */
  627.      
  628.      GDHandle mainDevicePtr;
  629.      
  630.      if ( mainScreenRectPtr != nil )
  631.      {
  632.           mainScreenRectPtr->top = 0;
  633.           mainScreenRectPtr->bottom = 342;
  634.           mainScreenRectPtr->left = 0;
  635.           mainScreenRectPtr->right = 512;  /* Hard coded data is wrong on the Lisa. But, I couldn’t rely of qd.screenBits.bounds as initialized */
  636.      
  637.           if ( IsColorQuickdrawAvailable ( ) != false )
  638.           {
  639.                mainDevicePtr = GetMainDevice ( );
  640.                
  641.                if ( mainDevicePtr != nil )
  642.                {
  643.                     *mainScreenRectPtr =  ((**((*mainDevicePtr)->gdPMap)).bounds);
  644.                }
  645.           }
  646.      }
  647. }
  648.  
  649.  
  650. Boolean IsPlotIconIDAvailable ( void )
  651. {
  652.      /*  96/01/11   v1.0.0   David Cook   First version  */
  653.  
  654.      Boolean doesPlotIconIDExist;
  655.      
  656.      
  657.      doesPlotIconIDExist = false;     
  658.     
  659.      if ( GetSystemVersion ( ) >= 0x0700 ) /* Well, it seems true. */
  660.      {
  661.           doesPlotIconIDExist = true;
  662.      }
  663.      
  664.      return ( doesPlotIconIDExist );
  665. }
  666.  
  667.  
  668.  
  669. /******************************** ICON AND CROSS OUT ROUTINES ***************************************/
  670.  
  671.  
  672. OSErr DrawIcon ( short iconResourceID, short pixelDepthOfScreenToDrawOn, Rect *pointerToDestinationRect )
  673. {   
  674.      /* If the pixelDepthOfScreenToDrawOn > 2 (i.e. more than 4 colors), this routine will     */
  675.      /* get and draw the icl8, icl4, cicn, OR ICN# (in that order) specified by iconResourceID */
  676.      /* in the native size (usually 32x32), at the position specified.                         */
  677.      
  678.      /* If the pixelDepthOfScreenToDrawOn <= 2 (i.e. 4 colors or black&white), this            */
  679.      /* routine will get and draw the ICN# specified by iconResourceID                         */
  680.      /* in the native size (usually 32x32), at the position specified.                         */
  681.      
  682.      /* If System 7 or better is installed, and an icl8, icl4, OR icn# is available,           */
  683.      /* PlotIconID will be called instead of using the rest of this function.                  */
  684.      
  685.      /* Note: This routine tries to find a 'cicn' ONLY if the pixelDepthOfScreenToDrawOn > 2   */
  686.      /* and neither an 'icl8' or 'icl4' of iconResourceID can be found.                        */
  687.           
  688.      /* If any of the icons are NOT purgable, they will be automatically purged when the       */
  689.      /* init’s resource fork is closed. This routine does NOT release the resource handles.    */
  690.      
  691.      /*  96/01/11   v1.0.0   David Cook   First version  */
  692.  
  693.      
  694.      
  695.      Rect iconSourceRect;
  696.      
  697.      CIconHandle colorIconHandle;  /* 'cicn' color icon handle            */
  698.      Handle iclHandle;             /* 'icl8' or 'icl4' color icon handle  */
  699.      Handle icnHandle;             /* 'ICN#' black&white icon handle      */
  700.      
  701.      PixMap iclPixMap;  /* Used for drawing 'icl8' or 'icl4' */
  702.      BitMap icnBitMap;  /* Used for drawing 'ICN#'s */
  703.      
  704.      GrafPtr localPortPtr;
  705.      
  706.      char originaliclHandleState;
  707.      char originalICNHandleState;
  708.      
  709.      short iclPixelDepth;
  710.      
  711.      OSErr result;
  712.      
  713.      
  714.      if ( pointerToDestinationRect == nil )
  715.      {
  716.           result = memPCErr;
  717.      }
  718.      else
  719.      {
  720.           result = noErr;
  721.               
  722.                         
  723.                
  724.           colorIconHandle = nil;
  725.           iclHandle = nil;
  726.      
  727.           if ( pixelDepthOfScreenToDrawOn >= kMinimumPixelDepthToDrawColorIcons
  728.                && IsColorQuickdrawAvailable ( ) != false ) /* Just checking to be safe! */
  729.           {
  730.                if ( pixelDepthOfScreenToDrawOn >= 8 )
  731.                {
  732.                     iclHandle = GetResource ('icl8', iconResourceID );
  733.                }
  734.           
  735.                if ( iclHandle != nil )
  736.                {
  737.                     iclPixelDepth = 8; /* An 'icl8' was successfully found */
  738.                }
  739.                else /* Either an 'icl8' wasn’t found or pixelDepthOfScreenToDrawOn < 8 */
  740.                {
  741.                     iclHandle = GetResource ('icl4', iconResourceID );
  742.                
  743.                     if ( iclHandle != nil ) /* An 'icl4' was successfully found */
  744.                     {
  745.                          iclPixelDepth = 4; /* An 'icl8' was successfully found */
  746.                     }
  747.                     else /* We haven’t found an 'icl8' or 'icl4' yet */
  748.                     {
  749.                          if ( pixelDepthOfScreenToDrawOn < 8 ) /* We haven’t tried looking for an 'icl8' yet */
  750.                          {
  751.                               iclHandle = GetResource ('icl8', iconResourceID );
  752.                          }
  753.                     
  754.                          if ( iclHandle != nil )
  755.                          {
  756.                               iclPixelDepth = 8;
  757.                          }
  758.                          else /* We’ve tried looking for an 'icl8' and an 'icl4' and the screen is color */
  759.                          {
  760.                               colorIconHandle = GetCIcon ( iconResourceID );
  761.                          }
  762.                     }
  763.                }          
  764.           }
  765.      
  766.      
  767.           if ( colorIconHandle != nil ) /* This can only be true if we couldn’t find an 'icl8' or an 'icl4' and the screen is color */
  768.           {          
  769.                PlotCIcon ( pointerToDestinationRect, colorIconHandle );
  770.                DisposCIcon ( colorIconHandle );
  771.                colorIconHandle = nil; /* Its a good habit to nil out local handles so you won’t mistakenly use them again and sometimes have it work. */
  772.           }
  773.           else
  774.           {
  775.                if ( IsPlotIconIDAvailable ( ) != false )
  776.                {
  777.                     result = PlotIconID ( pointerToDestinationRect, atNone, ttNone, iconResourceID );
  778.                }
  779.                else
  780.                {
  781.                     if ( iclHandle != nil ) /* If we have an 'icl8' or 'icl4' handle, lets make sure it doesn’t get purged before we use it */
  782.                     {
  783.                          originaliclHandleState = HGetState ( iclHandle );
  784.                          HNoPurge ( iclHandle );
  785.                     }
  786.                  
  787.                     GetPort ( &localPortPtr );
  788.           
  789.                     if ( localPortPtr == nil )
  790.                     {
  791.                          result = nilHandleErr;
  792.                     }
  793.                     else
  794.                     {     
  795.                          icnHandle = GetResource ('ICN#', iconResourceID );
  796.                          result = ResError ( );
  797.      
  798.                          if ( result == noErr )
  799.                          {
  800.                               if ( icnHandle == nil )
  801.                               {
  802.                                    result = resNotFound;  /* We were lied to by ResError - this does happen! */
  803.                               }
  804.                               else
  805.                               {
  806.                                    originalICNHandleState = HGetState ( icnHandle );
  807.                                    HNoPurge ( icnHandle );
  808.                     
  809.                                    iconSourceRect.left = 0;
  810.                                    iconSourceRect.right = kWidthOfIcon;
  811.                                    iconSourceRect.top = 0;
  812.                                    iconSourceRect.bottom = kHeightOfIcon;
  813.                      
  814.                                    if ( iclHandle != nil )
  815.                                    {
  816.                                         HLock ( iclHandle );
  817.                               
  818.                                         iclPixMap.baseAddr = *iclHandle;
  819.                                         iclPixMap.rowBytes = (((iconSourceRect.right - iconSourceRect.left) * iclPixelDepth) / 8) | 0x8000;
  820.                                         iclPixMap.bounds = iconSourceRect;
  821.                                         iclPixMap.pmVersion = 0;
  822.                                         iclPixMap.packType = 0;
  823.                                         iclPixMap.packSize = 0;
  824.                                         iclPixMap.hRes = 72;
  825.                                         iclPixMap.vRes = 72;
  826.                                         iclPixMap.pixelType = 0;
  827.                                         iclPixMap.pixelSize = iclPixelDepth;
  828.                                         iclPixMap.cmpCount = 1;
  829.                                         iclPixMap.cmpSize = iclPixelDepth;
  830.                                         iclPixMap.planeBytes = 0;
  831.                                         iclPixMap.pmTable = GetCTable ( iclPixelDepth );
  832.                                         iclPixMap.pmReserved = 0;
  833.                               
  834.                                         if ( iclPixMap.pmTable == nil ) /* Bummer! We were so close! */
  835.                                         {
  836.                                              HSetState ( iclHandle, originaliclHandleState );
  837.                                              iclHandle = nil;
  838.                                         }
  839.                                    }
  840.                          
  841.                                    HLock ( icnHandle );
  842.                        
  843.                                    icnBitMap.rowBytes = kBitmapRowBytesForBWIcon;
  844.                                    icnBitMap.bounds = iconSourceRect;
  845.                                    icnBitMap.baseAddr = *icnHandle + kByteOffsetToICNMask; /* punch hole with mask */
  846.  
  847.                                    CopyBits ( &icnBitMap, &(localPortPtr->portBits), &iconSourceRect, pointerToDestinationRect, srcBic, nil );
  848.                
  849.                                    if ( iclHandle != nil ) /* We have color */
  850.                                    {
  851.                                         CopyBits ( (BitMap *) &iclPixMap, &(localPortPtr->portBits), &iconSourceRect, pointerToDestinationRect, srcOr, nil );
  852.                                         DisposeCTable ( iclPixMap.pmTable );
  853.                                         iclPixMap.pmTable = nil;
  854.                                    }
  855.                                    else /* We don’t have color */
  856.                                    {
  857.                                         icnBitMap.baseAddr = *icnHandle; /* now draw the icon */
  858.                                         CopyBits ( &icnBitMap, &(localPortPtr->portBits), &iconSourceRect, pointerToDestinationRect, srcOr, nil );
  859.                                    }
  860.                      
  861.                                    HSetState ( icnHandle, originalICNHandleState );
  862.                               }
  863.                          }
  864.                     }
  865.                
  866.                     if ( iclHandle != nil )
  867.                     {
  868.                          HSetState ( iclHandle, originaliclHandleState );
  869.                     }
  870.                }
  871.           }
  872.      }              
  873.      
  874.      return ( result );
  875. }
  876.  
  877.  
  878.  
  879. void DrawCrossOut ( Rect *pointerToDestinationRect, short pixelDepthOfScreenToDrawOn )
  880. {
  881.      /* If we have a pointerToDestinationRect and is it NOT infinitely thin, this routine draws an 'X' (cross out) over the destination rect */
  882.      /* If pixelDepthOfScreenToDrawOn is not B&W (even if it is just 4 colors), the cross out will be attempted in color.           */
  883.      
  884.      /*  96/01/11   v1.0.0   David Cook   First version  */
  885.     
  886.      RGBColor workingRGBCrossOutColor;
  887.      
  888.      Pattern workingCrossOutPattern;
  889.      
  890.      
  891.      short widthOfDestinationRect;
  892.      short heightOfDestinationRect;
  893.      
  894.      if ( pointerToDestinationRect != nil ) /* Do we have a destinationRect? */
  895.      {
  896.           widthOfDestinationRect = pointerToDestinationRect->right - pointerToDestinationRect->left;
  897.           heightOfDestinationRect = pointerToDestinationRect->bottom - pointerToDestinationRect->top;
  898.           
  899.           if ( widthOfDestinationRect > 0 && heightOfDestinationRect > 0 )  /* Is the destinationRect NOT infinitely thin? */
  900.           {
  901.                if ( pixelDepthOfScreenToDrawOn > 1 )
  902.                {
  903.                     workingRGBCrossOutColor.red = 0;
  904.                     workingRGBCrossOutColor.green = 0;
  905.                     workingRGBCrossOutColor.blue = 0;
  906.                     
  907.                     RGBForeColor ( &workingRGBCrossOutColor );
  908.                }
  909.                
  910.                MoveTo ( pointerToDestinationRect->left + 2, pointerToDestinationRect->top );
  911.                Line ( widthOfDestinationRect - 3, heightOfDestinationRect - 3 );
  912.                Line ( -2, 2 );
  913.                Line ( -widthOfDestinationRect + 3, -heightOfDestinationRect + 3 );
  914.                Line ( 2, -2 );
  915.                
  916.                MoveTo ( pointerToDestinationRect->right - 3, pointerToDestinationRect->top );
  917.                Line ( 2, 2 );
  918.                Line ( -widthOfDestinationRect + 3, heightOfDestinationRect - 3 );
  919.                Line ( -2, -2 );
  920.                Line ( widthOfDestinationRect - 3, - heightOfDestinationRect + 3 );
  921.                
  922.                if ( pixelDepthOfScreenToDrawOn > 1 )
  923.                {
  924.                     workingRGBCrossOutColor.red = 65535;
  925.                     workingRGBCrossOutColor.green = 30000;
  926.                     workingRGBCrossOutColor.blue = 30000;
  927.                     
  928.                     RGBForeColor ( &workingRGBCrossOutColor );
  929.                }
  930.                
  931.                MoveTo ( pointerToDestinationRect->left + 2, pointerToDestinationRect->top + 1 );
  932.                Line ( -1, 1 );
  933.                Line ( widthOfDestinationRect - 5, heightOfDestinationRect - 5 ); 
  934.                
  935.                MoveTo ( pointerToDestinationRect->right - 3, pointerToDestinationRect->top + 1 );
  936.                Line ( -widthOfDestinationRect + 4, heightOfDestinationRect - 4 );
  937.                
  938.                if ( pixelDepthOfScreenToDrawOn > 1 )
  939.                {
  940.                     workingRGBCrossOutColor.red = 40000;
  941.                     workingRGBCrossOutColor.green = 0;
  942.                     workingRGBCrossOutColor.blue = 0;
  943.                     
  944.                     RGBForeColor ( &workingRGBCrossOutColor );
  945.                }
  946.                
  947.                Move ( 1, 1 );
  948.                Line ( widthOfDestinationRect - 4, -heightOfDestinationRect + 4 );
  949.                
  950.                MoveTo ( pointerToDestinationRect->left + 3, pointerToDestinationRect->top + 2 );
  951.                Line ( widthOfDestinationRect - 5, heightOfDestinationRect - 5 ); 
  952.                Line ( -1, 1 );
  953.                
  954.                if ( pixelDepthOfScreenToDrawOn > 1 )
  955.                {
  956.                     workingRGBCrossOutColor.red = 60000;
  957.                     workingRGBCrossOutColor.green = 0;
  958.                     workingRGBCrossOutColor.blue = 0;
  959.                     
  960.                     RGBForeColor ( &workingRGBCrossOutColor );
  961.                }
  962.                else
  963.                {
  964.                     workingCrossOutPattern.pat [ 0 ] = 0;
  965.                     workingCrossOutPattern.pat [ 1 ] = 0;
  966.                     workingCrossOutPattern.pat [ 2 ] = 0;
  967.                     workingCrossOutPattern.pat [ 3 ] = 0;
  968.                     workingCrossOutPattern.pat [ 4 ] = 0;
  969.                     workingCrossOutPattern.pat [ 5 ] = 0;
  970.                     workingCrossOutPattern.pat [ 6 ] = 0;
  971.                     workingCrossOutPattern.pat [ 7 ] = 0;
  972.                     
  973.                     PenPat ( &workingCrossOutPattern );
  974.                }
  975.                
  976.                MoveTo ( pointerToDestinationRect->left + 2, pointerToDestinationRect->top + 2 );
  977.                Line ( widthOfDestinationRect - 5, heightOfDestinationRect - 5 );
  978.                
  979.                MoveTo ( pointerToDestinationRect->right - 3, pointerToDestinationRect->top + 2 );
  980.                Line ( -widthOfDestinationRect + 5, heightOfDestinationRect - 5 );
  981.                
  982.                /* Set colors and pens back to black */
  983.                if ( pixelDepthOfScreenToDrawOn > 1 )
  984.                {
  985.                     workingRGBCrossOutColor.red = 0;
  986.                     workingRGBCrossOutColor.green = 0;
  987.                     workingRGBCrossOutColor.blue = 0;
  988.                     
  989.                     RGBForeColor ( &workingRGBCrossOutColor );
  990.                }
  991.                else
  992.                {
  993.                     /* If this routine is to stand alone, we can’t rely on the value of Quickdraw globals! */
  994.                     workingCrossOutPattern.pat [ 0 ] = 0xFF;
  995.                     workingCrossOutPattern.pat [ 1 ] = 0xFF;
  996.                     workingCrossOutPattern.pat [ 2 ] = 0xFF;
  997.                     workingCrossOutPattern.pat [ 3 ] = 0xFF;
  998.                     workingCrossOutPattern.pat [ 4 ] = 0xFF;
  999.                     workingCrossOutPattern.pat [ 5 ] = 0xFF;
  1000.                     workingCrossOutPattern.pat [ 6 ] = 0xFF;
  1001.                     workingCrossOutPattern.pat [ 7 ] = 0xFF;
  1002.                     PenPat ( &workingCrossOutPattern );
  1003.                }
  1004.           }
  1005.      }
  1006. }
  1007.  
  1008.  
  1009.  
  1010. /******************************** TEXT ROUTINES ***************************************/
  1011.  
  1012. void DrawTextCString ( char *pointerToTextCString, Rect *pointerToDestinationRect )
  1013. {        
  1014.      /* WARNING: Do NOT use this routine unless you have a port initialized! */
  1015.   
  1016.      /* This draws a c string (i.e. '0' terminated), in the specified style, in the specified destinationRect. */
  1017.      /* Passing nil for pointerToTextCString or pointerToDestinationRect will NOT result in a crash.           */
  1018.      
  1019.      /* 96/01/11   v1.0.0   David Cook   First version  */ 
  1020.      
  1021.      
  1022.      unsigned long numberOfCharactersInTextCString;     
  1023.      
  1024.      
  1025.      if ( pointerToTextCString != nil && *pointerToTextCString != 0      /* Make sure the c string exists and is not empty. */
  1026.           && pointerToDestinationRect != nil )                           /* Make sure destination rect exists.              */
  1027.      {
  1028.           TextFont ( kTextCStringFont );
  1029.           TextSize ( kTextCStringFontSize );
  1030.           TextFace ( kTextCStringFace );
  1031.           TextMode ( srcCopy );
  1032.           
  1033.           numberOfCharactersInTextCString = GetLengthOfCStringWithLimit ( pointerToTextCString, kTextCStringMaximumNumberOfCharacters );          
  1034.           TextBox ( pointerToTextCString, numberOfCharactersInTextCString, pointerToDestinationRect, kTextCStringAlignment );
  1035.      }
  1036. }
  1037.  
  1038.  
  1039. short GetPixelWidthOfTextCString ( char *pointerToTextCString )
  1040. {       
  1041.      /* WARNING: Do NOT use this routine unless you have a port initialized! */
  1042.      
  1043.      /* This sets the font and style appropriately, and then calculates the width of pointerToTextCString in pixels. */
  1044.      /* It does NOT restore the font and style to what it was before.                                                */
  1045.      
  1046.      /*  96/01/11   v1.0.0   David Cook   First version  */
  1047.  
  1048.      
  1049.      unsigned long numberOfCharactersInTextCString;     
  1050.      
  1051.      short textCStringWidthInPixels;
  1052.      
  1053.      
  1054.      textCStringWidthInPixels = 0;
  1055.      
  1056.      if ( pointerToTextCString != nil && *pointerToTextCString != 0 ) /* Make sure the c string exists and is not empty. */
  1057.      {
  1058.           TextFont ( kTextCStringFont );
  1059.           TextSize ( kTextCStringFontSize );
  1060.           TextFace ( kTextCStringFace );
  1061.           
  1062.           numberOfCharactersInTextCString = GetLengthOfCStringWithLimit ( pointerToTextCString, kTextCStringMaximumNumberOfCharacters );
  1063.           
  1064.           textCStringWidthInPixels = TextWidth ( pointerToTextCString, 0, numberOfCharactersInTextCString );          
  1065.      }
  1066.      
  1067.      return ( textCStringWidthInPixels );
  1068. }
  1069.  
  1070.  
  1071.  
  1072.  
  1073. /******************************** INIT ICON POSITIONING ROUTINES ***************************************/
  1074.  
  1075.  
  1076. void AdvanceShowInit ( short byHorizontalAmount )
  1077. {
  1078.      /* This routine advances the horizontal drawing position by the amount specified.     */
  1079.      /* Note: This does not move to an absolute position, but instead moves relative to    */
  1080.      /*       the current location.                                                        */
  1081.      
  1082.      /*  96/01/11   v1.0.0   David Cook   First version  */
  1083.      
  1084.      short horizontal,
  1085.            vertical;
  1086.            
  1087.      
  1088.      if ( byHorizontalAmount != 0 )
  1089.      {
  1090.           LMGetShowInitHorizontalAndVertical ( &horizontal, &vertical );
  1091.           
  1092.           horizontal += byHorizontalAmount + kHorizontalMarginBetweenShowInits;
  1093.                     
  1094.           CorrectShowInitHorizontalAndVerticalIfNotOnMainScreen ( &horizontal, kWidthOfIcon,
  1095.                                                                   &vertical,   kHeightOfIcon + kTextCStringHeight );
  1096.           /* Be good to the next icon being drawn and make sure they are on the screen and in the proper vertical position.          */
  1097.           /* This is also useful to nudge older algorithms that may not go high enough to avoid chopping off the top of our drawing. */
  1098.           
  1099.           LMSetShowInitHorizontalAndVertical ( horizontal, vertical );
  1100.      }
  1101. }
  1102.  
  1103.  
  1104. Boolean CorrectShowInitHorizontalAndVerticalIfNotOnMainScreen ( short *pointerToHorizontal, short horizontalSizeOfThisShowInit,
  1105.                                                                 short *pointerToVertical, short verticalSizeOfThisShowInit      )
  1106. {
  1107.      /* This routine makes sure that an init can display horizontalSizeOfThisShowInit pixels horizontally,    */
  1108.      /* and verticalSizeOfThisShowInit pixels vertically, and still be within the margins of the main screen. */
  1109.      /* Don’t add the standard kHorizontalMarginBetweenShowInits or kVerticalMarginBetweenShowInits to the    */
  1110.      /* sizes, that will be done automatically if necessary.                                                  */      
  1111.      
  1112.      /* If the drawing won’t fit within the horizontal screen width - margins, the vertical is advanced up    */
  1113.      /* a row up on the screen and the horizontal is reset to the beginning of the row + margin.              */
  1114.      /* If the init is at the top of the main screen, but still can’t fit, it                                 */
  1115.      /* is moved completely off the top of the screen so that it won’t drag its image partially over. You     */
  1116.      /* can’t simply move it to the next monitor horizontally, because this algorithm always resets the       */
  1117.      /* horizontal back onto the main monitor and then moves the vertical up.                                 */
  1118.          
  1119.      /* Notice that you can pass nil for pointerToHorizontal and pointerToVertical without bombing.           */
  1120.      
  1121.      /*  96/01/11   v1.0.0   David Cook   First version  */
  1122.      
  1123.      Rect mainScreenRect;
  1124.      
  1125.      Boolean aCorrectionWasMade;
  1126.      
  1127.      
  1128.      if ( verticalSizeOfThisShowInit < kMinimumVerticalSizeOfShowInit )
  1129.      {
  1130.           verticalSizeOfThisShowInit = kMinimumVerticalSizeOfShowInit;
  1131.      }
  1132.      
  1133.      GetMainScreenRect ( &mainScreenRect );
  1134.      
  1135.      mainScreenRect.left += kShowInitMarginForMainScreenLeft;
  1136.      mainScreenRect.right -= kShowInitMarginForMainScreenRight;
  1137.      /* mainScreenRect.top is a special case */
  1138.      mainScreenRect.bottom -= kShowInitMarginForMainScreenBottom;
  1139.      
  1140.      aCorrectionWasMade = false;
  1141.      
  1142.      
  1143.      if ( pointerToHorizontal != nil )
  1144.      {          
  1145.           if ( *pointerToHorizontal + horizontalSizeOfThisShowInit > mainScreenRect.right )
  1146.           {
  1147.                *pointerToHorizontal = mainScreenRect.left;
  1148.                               
  1149.                if ( pointerToVertical != nil )
  1150.                {
  1151.                     *pointerToVertical -= (verticalSizeOfThisShowInit + kVerticalMarginBetweenShowInits);
  1152.                }
  1153.                
  1154.                aCorrectionWasMade = true;
  1155.           }
  1156.           else if ( *pointerToHorizontal < mainScreenRect.left )
  1157.           {
  1158.                *pointerToHorizontal = mainScreenRect.left;
  1159.                
  1160.                aCorrectionWasMade = true;
  1161.           }
  1162.      }
  1163.      
  1164.      if ( pointerToVertical != nil )
  1165.      {
  1166.           if ( *pointerToVertical + verticalSizeOfThisShowInit > mainScreenRect.bottom )
  1167.           {
  1168.                *pointerToVertical = mainScreenRect.bottom - verticalSizeOfThisShowInit;
  1169.                
  1170.                aCorrectionWasMade = true;
  1171.           }
  1172.           else if (    *pointerToVertical < mainScreenRect.top + kShowInitMarginForMainScreenTop
  1173.                     && *pointerToVertical + verticalSizeOfThisShowInit > mainScreenRect.top - kShowInitMarginForMainScreenBottom )
  1174.                /* If our top is over the top margin of the screen, but our bottom is still partially on the main screen, take us all the way over the top of the screen */
  1175.           {
  1176.                *pointerToVertical = mainScreenRect.top - verticalSizeOfThisShowInit - kShowInitMarginForMainScreenBottom;
  1177.                /* Note: LMGetMBarHeight isn’t of value to us at startup, because there isn’t a menu bar yet. */
  1178.                
  1179.                aCorrectionWasMade = true;
  1180.           }            
  1181.      }
  1182.      
  1183.      return ( aCorrectionWasMade );
  1184. }
  1185.  
  1186.  
  1187.  
  1188. void LMGetShowInitHorizontalAndVertical ( short *pointerToHorizontal, short *pointerToVertical )
  1189. {
  1190.      /* This routine gets the horizontal and vertical position for drawing the current init.   */
  1191.      /* Note: You can pass a nil pointer in either of both parameters without causing a crash. */
  1192.      
  1193.      /*  96/01/11   v1.0.0   David Cook   First version  */
  1194.      
  1195.      StringPtr localCurApNamePtr;
  1196.      
  1197.      short storedHorizontal,
  1198.            storedVertical,
  1199.            storedChecksum;
  1200.      
  1201.      Rect mainScreenRect;
  1202.      
  1203.      
  1204.      GetMainScreenRect ( &mainScreenRect );
  1205.      
  1206.      
  1207.      localCurApNamePtr = LMGetCurApName ( );
  1208.      /* This won’t work correctly if LMGetCurApName returns a pointer to a copy of CurApName which has not been BlockMoved.      */
  1209.      /* (i.e. uses a Pascal string copy routine which fails to copy the normally “unused” bytes that follow a shorter filename.) */
  1210.           
  1211.      storedHorizontal = *( (short *) (localCurApNamePtr + kOffsetFromCurApNameToStoredHorizontal) );
  1212.      storedChecksum = *( (short *) (localCurApNamePtr + kOffsetFromCurApNameToStoredHorizontalChecksum) );
  1213.           
  1214.      if ( storedChecksum != CalculateHorizontalOrVerticalStorageChecksum ( storedHorizontal ) )
  1215.      {
  1216.           /* Since the checksum is NOT correct, we must be the very first init to be displayed.         */
  1217.           /* So, replace the incorrect horizontal position with the standard first horizontal position. */
  1218.           
  1219.           storedHorizontal = mainScreenRect.left + kDefaultHorizontalOffsetFromLeftOfMainScreenIfHorizontalChecksumIsWrong;
  1220.      }
  1221.      
  1222.      storedVertical = *( (short *) (localCurApNamePtr + kOffsetFromCurApNameToStoredVertical) );
  1223.      storedChecksum = *( (short *) (localCurApNamePtr + kOffsetFromCurApNameToStoredVerticalChecksum) );
  1224.           
  1225.      if ( storedChecksum != CalculateHorizontalOrVerticalStorageChecksum ( storedVertical ) )
  1226.      {
  1227.           /* Since the checksum is NOT correct, we must be the very first init to be displayed (or use this method). */
  1228.           /* So, replace the incorrect vertical position with the standard first vertical position.                  */
  1229.           
  1230.           storedVertical = mainScreenRect.bottom - kDefaultVerticalOffsetFromBottomOfMainScreenIfVerticalChecksumIsWrong;
  1231.      }
  1232.      
  1233.      
  1234.      if ( pointerToHorizontal != nil )
  1235.      {
  1236.           *pointerToHorizontal = storedHorizontal;
  1237.      }
  1238.      
  1239.      if ( pointerToVertical != nil )
  1240.      {
  1241.           *pointerToVertical = storedVertical;
  1242.      }
  1243. }
  1244.  
  1245.  
  1246. void LMSetShowInitHorizontalAndVertical ( short horizontal, short vertical )
  1247. {
  1248.      /* The routine stores the init’s horizontal and vertical position.       */
  1249.      /* (And updates the associated checksum.)                                */
  1250.      
  1251.      /* This routine does not advance the position, so if you don’t           */
  1252.      /* adjust the horizontal and vertical after you’re completely finished,  */
  1253.      /* the next init will draw over yours.                                   */
  1254.      
  1255.      /*  96/01/11   v1.0.0   David Cook   First version  */
  1256.      
  1257.      StringPtr localCurApNamePtr;
  1258.      
  1259.      
  1260.      localCurApNamePtr = LMGetCurApName ( );
  1261.      /* This won’t work correctly if LMGetCurApName returns a pointer to a copy of CurApName which has not been BlockMoved.      */
  1262.      /* (i.e. uses a Pascal string copy routine which fails to copy the normally “unused” bytes that follow a shorter filename.) */
  1263.           
  1264.      *( (short *) (localCurApNamePtr + kOffsetFromCurApNameToStoredHorizontal) ) = horizontal;
  1265.      *( (short *) (localCurApNamePtr + kOffsetFromCurApNameToStoredHorizontalChecksum) ) = CalculateHorizontalOrVerticalStorageChecksum ( horizontal );
  1266.      
  1267.      *( (short *) (localCurApNamePtr + kOffsetFromCurApNameToStoredVertical) ) = vertical;
  1268.      *( (short *) (localCurApNamePtr + kOffsetFromCurApNameToStoredVerticalChecksum) ) = CalculateHorizontalOrVerticalStorageChecksum ( vertical );
  1269.  
  1270.      LMSetCurApName ( localCurApNamePtr ); /* This might be necessary in future systems */
  1271.      /* This won’t work correctly if LMGetCurApName returns a pointer to a copy of CurApName, and LMSetCurApName doesn’t BlockMove it back. */
  1272.      /* (i.e. uses a Pascal string copy routine which fails to copy the normally “unused” bytes that follow a shorter filename.)            */
  1273. }
  1274.  
  1275.  
  1276. short CalculateHorizontalOrVerticalStorageChecksum ( short horizontalOrVerticalValue )
  1277. {
  1278.      /* Given the init’s horizontal or vertical position, this routine calculates and returns  */
  1279.      /* a checksum using the classic mathematical formula from the original ShowInit.a.         */
  1280.      
  1281.      /*  96/01/11   v1.0.0   David Cook   First version  */
  1282.      
  1283.      short storageChecksum;
  1284.      
  1285.      
  1286.      if ( ( horizontalOrVerticalValue & 0x8000 ) != 0 )
  1287.      {  
  1288.           storageChecksum = ( ( horizontalOrVerticalValue << 1 ) | 1 ) ^ kChecksumStorageConstant;
  1289.      }
  1290.      else
  1291.      {
  1292.           storageChecksum = ( horizontalOrVerticalValue << 1 ) ^ kChecksumStorageConstant;
  1293.      }
  1294.      
  1295.      return ( storageChecksum );
  1296. }